cmake_minimum_required(VERSION 3.16)
project(ConfigWeave VERSION 0.1.0 LANGUAGES CXX)

# C++ 标准，要求 C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_PREFIX_PATH "$ENV{CMAKE_PREFIX_PATH}")
include(GNUInstallDirs)

# 是否启用 YAML 支持
option(CFG_ENABLE_YAML "Enable YAML support" ON)

# 源码
add_library(configweave SHARED
    src/config.cpp
    src/pipeline.cpp
    src/configweave.cpp
    # 内置插件
    src/plugin/provider_single_file.cpp
    src/plugin/transformer_normalize_keys.cpp
    src/plugin/validator_required_key.cpp
    src/plugin/validators_common.cpp
    src/plugin/validators_combinators.cpp
    src/plugin/sink_stdout.cpp
    src/plugin/transformer_interpolate.cpp
)
add_library(ConfigWeave::configweave ALIAS configweave)

# 头文件包含路径
target_include_directories(configweave PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/configweave>
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/contrib/stlcompat/include>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

# C++17 兼容宏定义（关闭 C++20/23 专属特性）
target_compile_definitions(configweave PUBLIC
    CFG_HAVE_STD_EXPECTED=0
    CFG_HAVE_STD_SPAN=0
    CFG_HAVE_STD_FORMAT=0
    CFG_HAVE_STD_PRINT=0
)

# 在 C++20+ 下启用标准 ranges 支持，示例与部分便捷视图依赖它
if(CMAKE_CXX_STANDARD GREATER_EQUAL 20)
    target_compile_definitions(configweave PUBLIC CFG_HAVE_STD_RANGES=1)
else()
    target_compile_definitions(configweave PUBLIC CFG_HAVE_STD_RANGES=0)
endif()


# YAML 依赖
if(CFG_ENABLE_YAML)
    # 优先使用 Config 模式，其次使用模块模式
    find_package(yaml-cpp CONFIG QUIET)
    if(NOT yaml-cpp_FOUND)
        find_package(yaml-cpp QUIET)
    endif()

    if(yaml-cpp_FOUND)
        # 兼容不同导入方式（优先使用通用目标 yaml-cpp）
        if(TARGET yaml-cpp)
            target_link_libraries(configweave PUBLIC yaml-cpp)
        elseif(TARGET yaml-cpp::yaml-cpp)
            target_link_libraries(configweave PUBLIC yaml-cpp::yaml-cpp)
        elseif(DEFINED YAML_CPP_LIBRARIES)
            target_link_libraries(configweave PUBLIC ${YAML_CPP_LIBRARIES})
            if(DEFINED YAML_CPP_INCLUDE_DIR)
                target_include_directories(configweave PRIVATE ${YAML_CPP_INCLUDE_DIR})
            endif()
        else()
            message(FATAL_ERROR "yaml-cpp 已找到但未提供可链接目标，请检查包安装。")
        endif()
        target_compile_definitions(configweave PUBLIC CFG_ENABLE_YAML)
    else()
        message(FATAL_ERROR
            "CFG_ENABLE_YAML=ON 但未找到 yaml-cpp。请安装 yaml-cpp 或使用 -DCFG_ENABLE_YAML=OFF 关闭。")
    endif()
endif()


# 测试
option(CFG_BUILD_TESTS "Build tests" OFF)
if(CFG_BUILD_TESTS)
    enable_testing()

    # GTest 依赖
    find_package(GTest CONFIG QUIET)
    if(NOT GTest_FOUND)
        find_package(GTest QUIET)
    endif()
    if(NOT GTest_FOUND)
        message(FATAL_ERROR "未找到 GTest。请安装 googletest 或提供 CMAKE_PREFIX_PATH 指向其 cmake 包目录。")
    endif()
    
    add_executable(configweave_tests
        tests/view_tests.cpp
        tests/builder_basic_tests.cpp
        tests/include_file_tests.cpp
        tests/merge_object_tests.cpp
        tests/array_merge_tests.cpp
        tests/schema_tests.cpp
        tests/json_pointer_tests.cpp
        tests/pipeline_tests.cpp
    )

    # 确保测试目标也使用 C++17 兼容层（禁用 C++20/23 特性别名），避免 std::expected 的断言行为
    target_compile_definitions(configweave_tests PRIVATE
        CFG_HAVE_STD_EXPECTED=0
        CFG_HAVE_STD_SPAN=0
        CFG_HAVE_STD_FORMAT=0
        CFG_HAVE_STD_PRINT=0
        CFG_HAVE_STD_RANGES=0
    )

    # 兼容不同的 GTest 目标/变量
    set(_gtest_main_target "")
    if(TARGET GTest::gtest_main)
        set(_gtest_main_target GTest::gtest_main)
    elseif(TARGET GTest::Main)
        set(_gtest_main_target GTest::Main)
    elseif(TARGET gtest_main)
        set(_gtest_main_target gtest_main)
    endif()

    if(_gtest_main_target)
        target_link_libraries(configweave_tests PRIVATE 
            configweave
            ${_gtest_main_target}
        )
    elseif(DEFINED GTEST_MAIN_LIBRARIES OR DEFINED GTEST_LIBRARIES)
        target_link_libraries(configweave_tests PRIVATE 
            configweave
            ${GTEST_MAIN_LIBRARIES}
            ${GTEST_LIBRARIES}
        )
    else()
        message(FATAL_ERROR "GTest 已找到但未导出可用目标或变量，请检查 GTest 安装。")
    endif()

    target_include_directories(configweave_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)

    add_test(NAME configweave_tests COMMAND configweave_tests)
    set_tests_properties(configweave_tests PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()

# 示例程序
option(CFG_BUILD_EXAMPLES "Build examples" OFF)
if(CFG_BUILD_EXAMPLES)
    # 统一示例目标创建：减少重复的 add_executable/link/include 配置
    function(add_cfg_example target source)
        add_executable(${target} ${source})
        target_link_libraries(${target} PRIVATE configweave)
        target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
    endfunction()

    # 基础示例：schema 规则 + 校验 + 读取
    add_cfg_example(configweave_example_basic example/basic_schema_validation.cpp)

    # 视图示例：路径访问、数组下标、类型安全读取
    add_cfg_example(configweave_example_view example/view_and_lookup.cpp)

    # 文件加载：从 JSON/YAML 加载 schema 与配置（include_file）
    add_cfg_example(configweave_example_file_io example/file_loading_examples.cpp)

    # 错误场景：SchemaRootNotObject / IncludeOpenError / RegexMismatch 等
    add_cfg_example(configweave_example_errors example/error_scenarios.cpp)

    # 文件触发错误示例：SchemaRootNotObject / IncludeOpenError / IncludeParseError
    add_cfg_example(configweave_example_file_error_triggers example/file_error_triggers.cpp)

    # 文件规则联动的负例/边界用例
    add_cfg_example(configweave_example_file_validation_cases example/file_validation_cases.cpp)

    # 合并与冲突策略示例：浅/深合并 + 覆盖/保留/报错
    add_cfg_example(configweave_example_merge_conflict example/merge_and_conflict_policies.cpp)

    # 数组合并策略示例：Overwrite/Append/Deduplicate/Keyed
    add_cfg_example(configweave_example_array_merge example/array_merge_strategies.cpp)

    # 键规整与高级视图示例：normalize_keys + entries/object_view/JSON Pointer
    add_cfg_example(configweave_example_normalize_view example/normalization_and_view_advanced.cpp)

    # 最小流水线示例：single_file + normalize_keys + required_key
    add_cfg_example(configweave_example_pipeline example/pipeline.cpp)

    # 插件开发最小模板示例：包含 Provider/Transformer/Validator 骨架
    add_cfg_example(configweave_example_plugin_template example/plugin_template.cpp)

    # 组合校验器示例：all_of/any_of/one_of/not/if_then_else
    add_cfg_example(configweave_example_combinators example/combinator_validators.cpp)

    # 通用校验器示例：validators_common.cpp 中的所有校验器
    add_cfg_example(configweave_example_common_validators example/common_validators.cpp)

    # 一键运行所有示例程序（确保在构建后顺序执行每个示例）
    add_custom_target(run_examples
        DEPENDS
            configweave_example_basic
            configweave_example_view
            configweave_example_file_io
            configweave_example_errors
            configweave_example_file_error_triggers
            configweave_example_file_validation_cases
            configweave_example_merge_conflict
            configweave_example_array_merge
            configweave_example_normalize_view
            configweave_example_pipeline
            configweave_example_plugin_template
            configweave_example_combinators
            configweave_example_common_validators
        COMMAND ${CMAKE_COMMAND} -E echo "运行所有示例程序..."
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_basic>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_view>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_file_io>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_errors>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_file_error_triggers>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_file_validation_cases>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_merge_conflict>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_array_merge>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_normalize_view>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_pipeline>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_plugin_template>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_combinators>
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_FILE:configweave_example_common_validators>
    )
elseif(CFG_BUILD_EXAMPLES)
    message(STATUS "C++ standard is <20; examples use std::ranges/views, skipping examples in C++17 mode.")
endif()

# 安装与 Package 配置导出，支持 find_package(ConfigWeave)
include(CMakePackageConfigHelpers)
set(ConfigWeave_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/ConfigWeave")

set_target_properties(configweave PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION_MAJOR}
    EXPORT_NAME configweave
)

install(TARGETS configweave
    EXPORT ConfigWeaveTargets
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

install(EXPORT ConfigWeaveTargets
    FILE ConfigWeaveTargets.cmake
    NAMESPACE ConfigWeave::
    DESTINATION ${ConfigWeave_INSTALL_CMAKEDIR}
)

configure_package_config_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ConfigWeaveConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/ConfigWeaveConfig.cmake
    INSTALL_DESTINATION ${ConfigWeave_INSTALL_CMAKEDIR}
)

write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/ConfigWeaveConfigVersion.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/ConfigWeaveConfig.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/ConfigWeaveConfigVersion.cmake
    DESTINATION ${ConfigWeave_INSTALL_CMAKEDIR}
)

# 生成构建树下的导出文件，便于本地 find_package 使用（用户可选择使用 CMAKE_PREFIX_PATH 指向 build 目录）
export(EXPORT ConfigWeaveTargets
    FILE ${CMAKE_CURRENT_BINARY_DIR}/ConfigWeaveTargets.cmake
    NAMESPACE ConfigWeave::
)